 /*******************************************************************************
  * Copyright (c) 2000, 2007 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.ui.forms.widgets;

 import java.util.Enumeration ;
 import java.util.Hashtable ;
 import java.util.Vector ;

 import org.eclipse.swt.SWT;
 import org.eclipse.swt.graphics.*;
 import org.eclipse.swt.widgets.*;

 /**
  * This implementation of the layout algorithm attempts to position controls in
  * the composite using a two-pass autolayout HTML table altorithm recommeded by
  * HTML 4.01 W3C specification (see
  * http://www.w3.org/TR/html4/appendix/notes.html#h-B.5.2.2). The main
  * differences with GridLayout is that it has two passes and that width and
  * height are not calculated in the same pass.
  * <p>
  * The advantage of the algorithm over GridLayout is that it is capable of
  * flowing text controls capable of line wrap. These controls do not have
  * natural 'preferred size'. Instead, they are capable of providing the required
  * height if the width is set. Consequently, this algorithm first calculates the
  * widths that will be assigned to columns, and then passes those widths to the
  * controls to calculate the height. When a composite with this layout is a
  * child of the scrolling composite, they should interact in such a way that
  * reduction in the scrolling composite width results in the reflow and increase
  * of the overall height.
  * <p>
  * If none of the columns contain expandable and wrappable controls, the
  * end-result will be similar to the one provided by GridLayout. The difference
  * will show up for layouts that contain controls whose minimum and maximum
  * widths are not the same.
  *
  * @see TableWrapData
  * @since 3.0
  */
 public final class TableWrapLayout extends Layout implements ILayoutExtension {
     /**
      * Number of columns to use when positioning children (default is 1).
      */
     public int numColumns = 1;

     /**
      * Left margin variable (default is 5).
      */
     public int leftMargin = 5;

     /**
      * Right margin variable (default is 5).
      */
     public int rightMargin = 5;

     /**
      * Top margin variable (default is 5).
      */
     public int topMargin = 5;

     /**
      * Botom margin variable (default is 5).
      */
     public int bottomMargin = 5;

     /**
      * Horizontal spacing (default is 5).
      */
     public int horizontalSpacing = 5;

     /**
      * Vertical spacing (default is 5).
      */
     public int verticalSpacing = 5;

     /**
      * If set to <code>true</code>, all the columns will have the same width.
      * Otherwise, column widths will be computed based on controls in them and
      * their layout data (default is <code>false</code>).
      */
     public boolean makeColumnsEqualWidth = false;

     private boolean initialLayout = true;

     private Vector grid = null;

     private Hashtable rowspans;

     private int[] minColumnWidths, maxColumnWidths;

     private int widestColumnWidth;

     private int[] growingColumns;

     private int[] growingRows;

     private LayoutCache cache = new LayoutCache();

     private class RowSpan {
         Control child;

         int row;

         int column;

         int height;

         int totalHeight;

         public RowSpan(Control child, int column, int row) {
             this.child = child;
             this.column = column;
             this.row = row;
         }

         /*
          * Updates this row span's height with the given one if it is within
          * this span.
          */
         public void update(int currentRow, int rowHeight) {
             TableWrapData td = (TableWrapData) child.getLayoutData();
             // is currentRow within this span?
 if (currentRow >= row && currentRow < row + td.rowspan) {
                 totalHeight += rowHeight;
                 if (currentRow > row)
                     totalHeight += verticalSpacing;
             }
         }

         public int getRequiredHeightIncrease() {
             if (totalHeight < height)
                 return height - totalHeight;
             return 0;
         }
     }

     /**
      * Implements ILayoutExtension. Should not be called directly.
      *
      * @see ILayoutExtension
      */
     public int computeMinimumWidth(Composite parent, boolean changed) {

         Control[] children = parent.getChildren();
         if (changed) {
             cache.flush();
         }

         cache.setControls(children);

         changed = true;
         initializeIfNeeded(parent, changed);
         if (initialLayout) {
             changed = true;
             initialLayout = false;
         }
         if (grid == null || changed) {
             changed = true;
             grid = new Vector ();
             createGrid(parent);
         }
         if (minColumnWidths == null)
             minColumnWidths = new int[numColumns];
         for (int i = 0; i < numColumns; i++) {
             minColumnWidths[i] = 0;
         }
         return internalGetMinimumWidth(parent, changed);
     }

     /**
      * Implements ILayoutExtension. Should not be called directly.
      *
      * @see ILayoutExtension
      */
     public int computeMaximumWidth(Composite parent, boolean changed) {
         Control[] children = parent.getChildren();
         if (changed) {
             cache.flush();
         }

         cache.setControls(children);

         changed = true;
         initializeIfNeeded(parent, changed);
         if (initialLayout) {
             changed = true;
             initialLayout = false;
         }
         if (grid == null || changed) {
             changed = true;
             grid = new Vector ();
             createGrid(parent);
         }
         if (maxColumnWidths == null)
             maxColumnWidths = new int[numColumns];
         for (int i = 0; i < numColumns; i++) {
             maxColumnWidths[i] = 0;
         }
         return internalGetMaximumWidth(parent, changed);
     }

     /**
      * @see Layout#layout(Composite, boolean)
      */
     protected void layout(Composite parent, boolean changed) {

         Rectangle clientArea = parent.getClientArea();
         Control[] children = parent.getChildren();
         if (changed) {
             cache.flush();
         }

         if (children.length == 0)
             return;

         cache.setControls(children);

         int parentWidth = clientArea.width;
         changed = true;
         initializeIfNeeded(parent, changed);
         if (initialLayout) {
             changed = true;
             initialLayout = false;
         }
         if (grid == null || changed) {
             changed = true;
             grid = new Vector ();
             createGrid(parent);
         }
         resetColumnWidths();
         int minWidth = internalGetMinimumWidth(parent, changed);
         int maxWidth = internalGetMaximumWidth(parent, changed);
         int tableWidth = parentWidth;
         int[] columnWidths;
         if (parentWidth <= minWidth) {
             tableWidth = minWidth;
             if (makeColumnsEqualWidth) {
                 columnWidths = new int[numColumns];
                 for (int i = 0; i < numColumns; i++) {
                     columnWidths[i] = widestColumnWidth;
                 }
             } else
                 columnWidths = minColumnWidths;
         } else if (parentWidth > maxWidth) {
             if (growingColumns.length == 0) {
                 tableWidth = maxWidth;
                 columnWidths = maxColumnWidths;
             } else {
                 columnWidths = new int[numColumns];
                 int colSpace = tableWidth - leftMargin - rightMargin;
                 colSpace -= (numColumns - 1) * horizontalSpacing;
                 int extra = parentWidth - maxWidth;
                 int colExtra = extra / growingColumns.length;
                 for (int i = 0; i < numColumns; i++) {
                     columnWidths[i] = maxColumnWidths[i];
                     if (isGrowingColumn(i)) {
                         columnWidths[i] += colExtra;
                     }
                 }
             }
         } else {
             columnWidths = new int[numColumns];
             if (makeColumnsEqualWidth) {
                 int colSpace = tableWidth - leftMargin - rightMargin;
                 colSpace -= (numColumns - 1) * horizontalSpacing;
                 int col = colSpace / numColumns;
                 for (int i = 0; i < numColumns; i++) {
                     columnWidths[i] = col;
                 }
             } else {
                 columnWidths = assignExtraSpace(tableWidth, maxWidth, minWidth);
             }
         }
         int y = topMargin+clientArea.y;
         int[] rowHeights = computeRowHeights(children, columnWidths, changed);
         for (int i = 0; i < grid.size(); i++) {
             int rowHeight = rowHeights[i];
             int x = leftMargin+clientArea.x;
             TableWrapData[] row = (TableWrapData[]) grid.elementAt(i);
             for (int j = 0; j < numColumns; j++) {
                 TableWrapData td = row[j];
                 if (td.isItemData) {
                     Control child = children[td.childIndex];
                     placeControl(child, td, x, y, rowHeights, i);
                 }
                 x += columnWidths[j];
                 if (j < numColumns - 1)
                     x += horizontalSpacing;
             }
             y += rowHeight + verticalSpacing;
         }
     }

     int[] computeRowHeights(Control[] children, int[] columnWidths,
             boolean changed) {
         int[] rowHeights = new int[grid.size()];
         for (int i = 0; i < grid.size(); i++) {
             TableWrapData[] row = (TableWrapData[]) grid.elementAt(i);
             rowHeights[i] = 0;
             for (int j = 0; j < numColumns; j++) {
                 TableWrapData td = row[j];
                 if (td.isItemData == false) {
                     continue;
                 }
                 Control child = children[td.childIndex];
                 int span = td.colspan;
                 int cwidth = 0;
                 for (int k = j; k < j + span; k++) {
                     cwidth += columnWidths[k];
                     if (k < j + span - 1)
                         cwidth += horizontalSpacing;
                 }
                 Point size = computeSize(td.childIndex, cwidth, td.indent, td.maxWidth, td.maxHeight);
                 td.compWidth = cwidth;
                 if (td.heightHint != SWT.DEFAULT) {
                     size = new Point(size.x, td.heightHint);
                 }
                 td.compSize = size;
                 RowSpan rowspan = (RowSpan) rowspans.get(child);
                 if (rowspan == null) {
                     rowHeights[i] = Math.max(rowHeights[i], size.y);
                 } else
                     rowspan.height = size.y;
             }
             updateRowSpans(i, rowHeights[i]);
         }
         for (Enumeration enm = rowspans.elements(); enm.hasMoreElements();) {
             RowSpan rowspan = (RowSpan) enm.nextElement();
             int increase = rowspan.getRequiredHeightIncrease();
             if (increase == 0)
                 continue;
             TableWrapData td = (TableWrapData) rowspan.child.getLayoutData();
             int ngrowing = 0;
             int[] affectedRows = new int[grid.size()];
             for (int i = 0; i < growingRows.length; i++) {
                 int growingRow = growingRows[i];
                 if (growingRow >= rowspan.row
                         && growingRow < rowspan.row + td.rowspan) {
                     affectedRows[ngrowing++] = growingRow;
                 }
             }
             if (ngrowing == 0) {
                 ngrowing = 1;
                 affectedRows[0] = rowspan.row + td.rowspan - 1;
             }
             increase += increase % ngrowing;
             int perRowIncrease = increase / ngrowing;
             for (int i = 0; i < ngrowing; i++) {
                 int growingRow = affectedRows[i];
                 rowHeights[growingRow] += perRowIncrease;
             }
         }
         return rowHeights;
     }

     boolean isGrowingColumn(int col) {
         if (growingColumns == null)
             return false;
         for (int i = 0; i < growingColumns.length; i++) {
             if (col == growingColumns[i])
                 return true;
         }
         return false;
     }

     int[] assignExtraSpace(int tableWidth, int maxWidth, int minWidth) {
         int fixedPart = leftMargin + rightMargin + (numColumns - 1)
                 * horizontalSpacing;
         int D = maxWidth - minWidth;
         int W = tableWidth - fixedPart - minWidth;
         int widths[] = new int[numColumns];
         int rem = 0;
         for (int i = 0; i < numColumns; i++) {
             int cmin = minColumnWidths[i];
             int cmax = maxColumnWidths[i];
             int d = cmax - cmin;
             int extra = D != 0 ? (d * W) / D : 0;
             if (i < numColumns - 1) {
                 widths[i] = cmin + extra;
                 rem += widths[i];
             } else {
                 widths[i] = tableWidth - fixedPart - rem;
             }
         }
         return widths;
     }

     Point computeSize(int childIndex, int width, int indent, int maxWidth, int maxHeight) {
         int widthArg = width - indent;
         SizeCache controlCache = cache.getCache(childIndex);
         if (!isWrap(controlCache.getControl()))
             widthArg = SWT.DEFAULT;
         Point size = controlCache.computeSize(widthArg, SWT.DEFAULT);
         if (maxWidth!=SWT.DEFAULT)
             size.x = Math.min(size.x, maxWidth);
         if (maxHeight!=SWT.DEFAULT)
             size.y = Math.min(size.y, maxHeight);
         size.x += indent;
         return size;
     }

     void placeControl(Control control, TableWrapData td, int x, int y,
             int[] rowHeights, int row) {
         int xloc = x + td.indent;
         int yloc = y;
         int height = td.compSize.y;
         int colWidth = td.compWidth - td.indent;
         int width = td.compSize.x-td.indent;
         width = Math.min(width, colWidth);
         int slotHeight = rowHeights[row];
         RowSpan rowspan = (RowSpan) rowspans.get(control);
         if (rowspan != null) {
             slotHeight = 0;
             for (int i = row; i < row + td.rowspan; i++) {
                 if (i > row)
                     slotHeight += verticalSpacing;
                 slotHeight += rowHeights[i];
             }
         }
         // align horizontally
 if (td.align == TableWrapData.CENTER) {
             xloc = x + colWidth / 2 - width / 2;
         } else if (td.align == TableWrapData.RIGHT) {
             xloc = x + colWidth - width;
         } else if (td.align == TableWrapData.FILL) {
             width = colWidth;
         }
         // align vertically
 if (td.valign == TableWrapData.MIDDLE) {
             yloc = y + slotHeight / 2 - height / 2;
         } else if (td.valign == TableWrapData.BOTTOM) {
             yloc = y + slotHeight - height;
         } else if (td.valign == TableWrapData.FILL) {
             height = slotHeight;
         }
         control.setBounds(xloc, yloc, width, height);
     }

     void createGrid(Composite composite) {
         int row, column, rowFill, columnFill;
         Control[] children;
         TableWrapData spacerSpec;
         Vector growingCols = new Vector ();
         Vector growingRows = new Vector ();
         rowspans = new Hashtable ();
         //
 children = composite.getChildren();
         if (children.length == 0)
             return;
         //
 grid.addElement(createEmptyRow());
         row = 0;
         column = 0;
         // Loop through the children and place their associated layout specs in
 // the
 // grid. Placement occurs left to right, top to bottom (i.e., by row).
 for (int i = 0; i < children.length; i++) {
             // Find the first available spot in the grid.
 Control child = children[i];
             TableWrapData spec = (TableWrapData) child.getLayoutData();
             while (((TableWrapData[]) grid.elementAt(row))[column] != null) {
                 column = column + 1;
                 if (column >= numColumns) {
                     row = row + 1;
                     column = 0;
                     if (row >= grid.size()) {
                         grid.addElement(createEmptyRow());
                     }
                 }
             }
             // See if the place will support the widget's horizontal span. If
 // not, go to the
 // next row.
 if (column + spec.colspan - 1 >= numColumns) {
                 grid.addElement(createEmptyRow());
                 row = row + 1;
                 column = 0;
             }
             // The vertical span for the item will be at least 1. If it is > 1,
 // add other rows to the grid.
 if (spec.rowspan > 1) {
                 rowspans.put(child, new RowSpan(child, column, row));
             }
             for (int j = 2; j <= spec.rowspan; j++) {
                 if (row + j > grid.size()) {
                     grid.addElement(createEmptyRow());
                 }
             }
             // Store the layout spec. Also cache the childIndex. NOTE: That we
 // assume the children of a
 // composite are maintained in the order in which they are created
 // and added to the composite.
 ((TableWrapData[]) grid.elementAt(row))[column] = spec;
             spec.childIndex = i;
             if (spec.grabHorizontal) {
                 updateGrowingColumns(growingCols, spec, column);
             }
             if (spec.grabVertical) {
                 updateGrowingRows(growingRows, spec, row);
             }
             // Put spacers in the grid to account for the item's vertical and
 // horizontal
 // span.
 rowFill = spec.rowspan - 1;
             columnFill = spec.colspan - 1;
             for (int r = 1; r <= rowFill; r++) {
                 for (int c = 0; c < spec.colspan; c++) {
                     spacerSpec = new TableWrapData();
                     spacerSpec.isItemData = false;
                     ((TableWrapData[]) grid.elementAt(row + r))[column + c] = spacerSpec;
                 }
             }
             for (int c = 1; c <= columnFill; c++) {
                 for (int r = 0; r < spec.rowspan; r++) {
                     spacerSpec = new TableWrapData();
                     spacerSpec.isItemData = false;
                     ((TableWrapData[]) grid.elementAt(row + r))[column + c] = spacerSpec;
                 }
             }
             column = column + spec.colspan - 1;
         }
         // Fill out empty grid cells with spacers.
 for (int k = column + 1; k < numColumns; k++) {
             spacerSpec = new TableWrapData();
             spacerSpec.isItemData = false;
             ((TableWrapData[]) grid.elementAt(row))[k] = spacerSpec;
         }
         for (int k = row + 1; k < grid.size(); k++) {
             spacerSpec = new TableWrapData();
             spacerSpec.isItemData = false;
             ((TableWrapData[]) grid.elementAt(k))[column] = spacerSpec;
         }
         growingColumns = new int[growingCols.size()];
         for (int i = 0; i < growingCols.size(); i++) {
             growingColumns[i] = ((Integer ) growingCols.get(i)).intValue();
         }
         this.growingRows = new int[growingRows.size()];
         for (int i = 0; i < growingRows.size(); i++) {
             this.growingRows[i] = ((Integer ) growingRows.get(i)).intValue();
         }
     }

     private void updateGrowingColumns(Vector growingColumns,
             TableWrapData spec, int column) {
         int affectedColumn = column + spec.colspan - 1;
         for (int i = 0; i < growingColumns.size(); i++) {
             Integer col = (Integer ) growingColumns.get(i);
             if (col.intValue() == affectedColumn)
                 return;
         }
         growingColumns.add(new Integer (affectedColumn));
     }

     private void updateGrowingRows(Vector growingRows, TableWrapData spec,
             int row) {
         int affectedRow = row + spec.rowspan - 1;
         for (int i = 0; i < growingRows.size(); i++) {
             Integer irow = (Integer ) growingRows.get(i);
             if (irow.intValue() == affectedRow)
                 return;
         }
         growingRows.add(new Integer (affectedRow));
     }

     private TableWrapData[] createEmptyRow() {
         TableWrapData[] row = new TableWrapData[numColumns];
         for (int i = 0; i < numColumns; i++)
             row[i] = null;
         return row;
     }

     /**
      * @see Layout#computeSize(Composite, int, int, boolean)
      */
     protected Point computeSize(Composite parent, int wHint, int hHint,
             boolean changed) {
         Control[] children = parent.getChildren();
         if (changed) {
             cache.flush();
         }
         if (children.length == 0) {
             return new Point(0, 0);
         }
         cache.setControls(children);

         int parentWidth = wHint;
         changed = true;
         initializeIfNeeded(parent, changed);
         if (initialLayout) {
             changed = true;
             initialLayout = false;
         }
         if (grid == null || changed) {
             changed = true;
             grid = new Vector ();
             createGrid(parent);
         }
         resetColumnWidths();
         int minWidth = internalGetMinimumWidth(parent, changed);
         int maxWidth = internalGetMaximumWidth(parent, changed);
         
         if (wHint == SWT.DEFAULT)
             parentWidth = maxWidth;
         
         int tableWidth = parentWidth;
         int[] columnWidths;
         if (parentWidth <= minWidth) {
             tableWidth = minWidth;
             if (makeColumnsEqualWidth) {
                 columnWidths = new int[numColumns];
                 for (int i = 0; i < numColumns; i++) {
                     columnWidths[i] = widestColumnWidth;
                 }
             } else
                 columnWidths = minColumnWidths;
         } else if (parentWidth >= maxWidth) {
             if (makeColumnsEqualWidth) {
                 columnWidths = new int[numColumns];
                 int colSpace = parentWidth - leftMargin - rightMargin;
                 colSpace -= (numColumns - 1) * horizontalSpacing;
                 int col = colSpace / numColumns;
                 for (int i = 0; i < numColumns; i++) {
                     columnWidths[i] = col;
                 }
             } else {
                 tableWidth = maxWidth;
                 columnWidths = maxColumnWidths;
             }
         } else {
             columnWidths = new int[numColumns];
             if (makeColumnsEqualWidth) {
                 int colSpace = tableWidth - leftMargin - rightMargin;
                 colSpace -= (numColumns - 1) * horizontalSpacing;
                 int col = colSpace / numColumns;
                 for (int i = 0; i < numColumns; i++) {
                     columnWidths[i] = col;
                 }
             } else {
                 columnWidths = assignExtraSpace(tableWidth, maxWidth, minWidth);
             }
         }
         int totalHeight = 0;
         int innerHeight = 0;
         // compute widths
 for (int i = 0; i < grid.size(); i++) {
             TableWrapData[] row = (TableWrapData[]) grid.elementAt(i);
             // assign widths, calculate heights
 int rowHeight = 0;
             for (int j = 0; j < numColumns; j++) {
                 TableWrapData td = row[j];
                 if (td.isItemData == false) {
                     continue;
                 }
                 Control child = children[td.childIndex];
                 int span = td.colspan;
                 int cwidth = 0;
                 for (int k = j; k < j + span; k++) {
                     if (k > j)
                         cwidth += horizontalSpacing;
                     cwidth += columnWidths[k];
                 }
                 int cy = td.heightHint;
                 if (cy == SWT.DEFAULT) {
                     Point size = computeSize(td.childIndex, cwidth, td.indent, td.maxWidth, td.maxHeight);
                     cy = size.y;
                 }
                 RowSpan rowspan = (RowSpan) rowspans.get(child);
                 if (rowspan != null) {
                     // don't take the height of this child into acount
 // because it spans multiple rows
 rowspan.height = cy;
                 } else {
                     rowHeight = Math.max(rowHeight, cy);
                 }
             }
             updateRowSpans(i, rowHeight);
             if (i > 0)
                 innerHeight += verticalSpacing;
             innerHeight += rowHeight;
         }
         if (!rowspans.isEmpty())
             innerHeight = compensateForRowSpans(innerHeight);
         totalHeight = topMargin + innerHeight + bottomMargin;
         return new Point(tableWidth, totalHeight);
     }

     private void updateRowSpans(int row, int rowHeight) {
         if (rowspans == null || rowspans.size() == 0)
             return;
         for (Enumeration enm = rowspans.elements(); enm.hasMoreElements();) {
             RowSpan rowspan = (RowSpan) enm.nextElement();
             rowspan.update(row, rowHeight);
         }
     }

     private int compensateForRowSpans(int totalHeight) {
         for (Enumeration enm = rowspans.elements(); enm.hasMoreElements();) {
             RowSpan rowspan = (RowSpan) enm.nextElement();
             totalHeight += rowspan.getRequiredHeightIncrease();
         }
         return totalHeight;
     }

     int internalGetMinimumWidth(Composite parent, boolean changed) {
         if (changed)
             //calculateMinimumColumnWidths(parent, true);
 calculateColumnWidths(parent, minColumnWidths, false, true);
         int minimumWidth = 0;
         widestColumnWidth = 0;
         if (makeColumnsEqualWidth) {
             for (int i = 0; i < numColumns; i++) {
                 widestColumnWidth = Math.max(widestColumnWidth,
                         minColumnWidths[i]);
             }
         }
         for (int i = 0; i < numColumns; i++) {
             if (i > 0)
                 minimumWidth += horizontalSpacing;
             if (makeColumnsEqualWidth)
                 minimumWidth += widestColumnWidth;
             else
                 minimumWidth += minColumnWidths[i];
         }
         // add margins
 minimumWidth += leftMargin + rightMargin;
         return minimumWidth;
     }

     int internalGetMaximumWidth(Composite parent, boolean changed) {
         if (changed)
             //calculateMaximumColumnWidths(parent, true);
 calculateColumnWidths(parent, maxColumnWidths, true, true);
         int maximumWidth = 0;
         for (int i = 0; i < numColumns; i++) {
             if (i > 0)
                 maximumWidth += horizontalSpacing;
             maximumWidth += maxColumnWidths[i];
         }
         // add margins
 maximumWidth += leftMargin + rightMargin;
         return maximumWidth;
     }

     void resetColumnWidths() {
         if (minColumnWidths == null)
             minColumnWidths = new int[numColumns];
         if (maxColumnWidths == null)
             maxColumnWidths = new int[numColumns];
         for (int i = 0; i < numColumns; i++) {
             minColumnWidths[i] = 0;
         }
         for (int i = 0; i < numColumns; i++) {
             maxColumnWidths[i] = 0;
         }
     }
     
     void calculateColumnWidths(Composite parent, int [] columnWidths, boolean max, boolean changed) {
         boolean secondPassNeeded=false;
         for (int i = 0; i < grid.size(); i++) {
             TableWrapData[] row = (TableWrapData[]) grid.elementAt(i);
             for (int j = 0; j < numColumns; j++) {
                 TableWrapData td = row[j];
                 if (td.isItemData == false)
                     continue;
                 
                 if (td.colspan>1) {
                     // we will not do controls with multiple column span
 // here - increment and continue
 secondPassNeeded=true;
                     j+=td.colspan-1;
                     continue;
                 }

                 SizeCache childCache = cache.getCache(td.childIndex);
                 // !!
 int width = max?childCache.computeMaximumWidth():childCache.computeMinimumWidth();
                 if (td.maxWidth!=SWT.DEFAULT)
                     width = Math.min(width, td.maxWidth);

                 width += td.indent;
                 columnWidths[j] = Math.max(columnWidths[j], width);
             }
         }
         if (!secondPassNeeded) return;
         
         // Second pass for controls with multi-column horizontal span
 for (int i = 0; i < grid.size(); i++) {
             TableWrapData[] row = (TableWrapData[]) grid.elementAt(i);
             for (int j = 0; j < numColumns; j++) {
                 TableWrapData td = row[j];
                 if (td.isItemData == false || td.colspan==1)
                     continue;

                 SizeCache childCache = cache.getCache(td.childIndex);
                 int width = max?childCache.computeMaximumWidth():childCache.computeMinimumWidth();
                 if (td.maxWidth!=SWT.DEFAULT)
                     width = Math.min(width, td.maxWidth);

                 width += td.indent;
                 // check if the current width is enough to
 // support the control; if not, add the delta to
 // the last column or to all the growing columns, if present
 int current = 0;
                 for (int k = j; k < j + td.colspan; k++) {
                     if (k > j)
                         current += horizontalSpacing;
                     current += columnWidths[k];
                 }
                 if (width <= current) {
                     // we are ok - nothing to do here
 } else {
                     int ndiv = 0;
                     if (growingColumns != null) {
                         for (int k = j; k < j + td.colspan; k++) {
                             if (isGrowingColumn(k)) {
                                 ndiv++;
                             }
                         }
                     }
                     if (ndiv == 0) {
                         // add the delta to the last column
 columnWidths[j + td.colspan - 1] += width
                                 - current;
                     } else {
                         // distribute the delta to the growing
 // columns
 int percolumn = (width - current) / ndiv;
                         if ((width - current) % ndiv > 0)
                             percolumn++;
                         for (int k = j; k < j + td.colspan; k++) {
                             if (isGrowingColumn(k))
                                 columnWidths[k] += percolumn;
                         }
                     }
                 }
             }
         }
     }
     
     boolean isWrap(Control control) {
         if (control instanceof Composite
                 && ((Composite) control).getLayout() instanceof ILayoutExtension)
             return true;
         return (control.getStyle() & SWT.WRAP) != 0;
     }

     private void initializeIfNeeded(Composite parent, boolean changed) {
         if (changed)
             initialLayout = true;
         if (initialLayout) {
             initializeLayoutData(parent);
             initialLayout = false;
         }
     }

     void initializeLayoutData(Composite composite) {
         Control[] children = composite.getChildren();
         for (int i = 0; i < children.length; i++) {
             Control child = children[i];
             if (child.getLayoutData() == null) {
                 child.setLayoutData(new TableWrapData());
             }
         }
     }
 }

